/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.je.bpm.engine.impl.cmd;

import com.alibaba.fastjson2.JSONObject;
import com.google.common.base.Strings;
import com.je.bpm.core.model.SequenceFlow;
import com.je.bpm.core.model.button.*;
import com.je.bpm.core.model.button.factory.ButtonEnum;
import com.je.bpm.engine.button.validator.ButtonValidateParam;
import com.je.bpm.engine.delegate.DelegateExecution;
import com.je.bpm.engine.delegate.DelegateHelper;
import com.je.bpm.engine.impl.bpmn.behavior.KaiteBaseUserTaskActivityBehavior;
import com.je.bpm.engine.impl.interceptor.Command;
import com.je.bpm.engine.impl.interceptor.CommandContext;
import com.je.bpm.engine.impl.persistence.entity.*;
import com.je.bpm.engine.task.DelegationState;
import com.je.bpm.engine.task.IdentityLinkType;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 根据流程绑定的funcCode获取流程未时可展示的按钮
 */
public class GetRunTaskButtonsCmd implements Command<List<Button>>, Serializable {

    private static final long serialVersionUID = 1L;
    protected VariableInstanceEntity buttonVariable;
    protected TaskEntityImpl taskEntity;
    protected ButtonValidateParam buttonValidateParam;
    private PassRoundEntityManager passRoundEntityManager;
    private String processInstanceId;
    private String participantUserIds = "";

    public GetRunTaskButtonsCmd(VariableInstanceEntity buttonVariable, TaskEntityImpl taskEntity, ButtonValidateParam buttonValidateParam) {
        this.buttonVariable = buttonVariable;
        this.taskEntity = taskEntity;
        this.buttonValidateParam = buttonValidateParam;
        this.processInstanceId = taskEntity.getProcessInstanceId();
    }

    @Override
    public List<Button> execute(CommandContext commandContext) {
        passRoundEntityManager = commandContext.getProcessEngineConfiguration().getPassRoundEntityManager();
        if (Strings.isNullOrEmpty(taskEntity.getAssignee()) && !Strings.isNullOrEmpty(processInstanceId)) {
            List<IdentityLinkEntity> linkEntities = commandContext.getIdentityLinkEntityManager().findIdentityLinksByTaskId(taskEntity.getId());
            participantUserIds = linkEntities.stream().filter(identityLinkEntity -> identityLinkEntity.getType().equals(IdentityLinkType.CANDIDATE))
                    .map(identityLinkEntity -> identityLinkEntity.getUserId()).collect(Collectors.joining(","));
        }
        return getRunTaskButton(commandContext);
    }

    /**
     * 拿到真实的按钮button
     * 流程管理员级别的不会在流程变量里面，需要特殊处理，加进去
     *
     * @return
     */
    private List<Button> getRunTaskButton(CommandContext commandContext) {
        List<Button> list = new ArrayList<>();
        if (taskEntity == null) {
            return list;
        }
        List<String> buttonCodes = new ArrayList<>();

        //会签已审批
        Boolean isCountersignApproval = DelegateHelper.isCountersignApproval(taskEntity);
        List<String> participantList = new ArrayList<>();
        if (!Strings.isNullOrEmpty(participantUserIds)) {
            participantList = Arrays.asList(participantUserIds.split(","));
        }

        //审阅按钮
        String loginUserId = buttonValidateParam.getLogUserId();
        List<PassRoundEntity> passRoundEntities = passRoundEntityManager.findByToAndNoReview(taskEntity.getProcessInstanceId(), loginUserId);
        if (passRoundEntities.size() >= 1) {
            buttonCodes.add(ButtonEnum.PASSROUND_READ_BTN.getCode());
            list.addAll(TaskButton.getButtonsByCode(buttonCodes));
        }

        if (!(DelegateHelper.isHandler(taskEntity, taskEntity.getAssignee(), taskEntity.getOwner(), participantList, true, false))) {
            return list;
        }

        if (isCountersignApproval) {
            buttonCodes.add(ButtonEnum.COUNTERSIGNED_REBOOK_REDUCTION_BTN.getCode());
        }

        //当前登录人是流程处理人，需要获取按钮
        if (buttonValidateParam.getLogUserId().equals(taskEntity.getAssignee()) ||
                participantUserIds.contains(buttonValidateParam.getLogUserId())) {
            if (Strings.isNullOrEmpty(participantUserIds)) {
                buttonCodes = TaskButton.buildTaskButton(buttonVariable.getTextValue());
            } else {
                buttonCodes.add(ButtonEnum.RECEIVE_BTN.getCode());
            }
        }

        //委托处理，判断是否有取消委托按钮
        Boolean isEntrust = false;
        if (taskEntity.getDelegationState() != null && taskEntity.getDelegationState().equals(DelegationState.PENDING)) {
            String owner = taskEntity.getOwner();
            if (buttonValidateParam.getLogUserId().equals(owner)) {
                buttonCodes.add(ButtonEnum.CANCEL_DELEGATE_BTN.getCode());
                isEntrust = true;
            }
        }

        //前置节点处理按钮,取回，催办...
        buttonCodes.addAll(buildBeforeTaskButton(taskEntity, buttonVariable.getTextValue(), buttonValidateParam.getLogUserId(), isEntrust));
        //任务级别按钮（只要变量config里面有，就会加载）
        list.addAll(TaskButton.getButtonsByCode(buttonCodes));
        //流程普通员工级别按钮（任务变量config里面不会添加管理员级别的按钮code）
        list.addAll(ProcessButton.getButtonsByCode(buttonCodes));
        //流程管理员级别的按钮 //TODO 另外一个命令去做处理
        //去重，有管理员和普通员工可能拥有重复的按钮（比如：作废）
        list = list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
                new TreeSet<>(Comparator.comparing(Button::getCode))), ArrayList::new));
        addCustomCommitButton(list, commandContext);
        //领取任务


        return list;
    }

    private static final String FORM_VAR_NAME = "form";
    private static final String PREV_ASSIGNEE = "prevAssignee";
    private static final String BUTTON_VAR_NAME = "buttons";
    private static final String PREVIOUS = "previous";

    /**
     * 解析json拿到真实的按钮buttonCode
     *
     * @param text
     * @return
     */
    public static List<String> buildBeforeTaskButton(TaskEntityImpl taskEntity, String text, String logUserId, Boolean isEntrust) {
        List<String> buttonCodes = new ArrayList<>();
        if (Strings.isNullOrEmpty(text)) {
            return buttonCodes;
        }
        String prevAssignee = JSONObject.parseObject(text).getJSONObject(FORM_VAR_NAME).getString(PREV_ASSIGNEE);
        if (Strings.isNullOrEmpty(prevAssignee) || !prevAssignee.equals(logUserId)) {
            Boolean isPrevAssignee = false;
            Object prevAssigneeObject = taskEntity.getVariable(KaiteBaseUserTaskActivityBehavior.PREV_ASSIGNEE);
            if (prevAssigneeObject != null) {
                String prevAssigneeUserId = (String) prevAssigneeObject;
                if (prevAssigneeUserId.equals(logUserId) && taskEntity.getAssignee().equals(logUserId)) {
                    isPrevAssignee = true;
                }
            }
            if (!isPrevAssignee) {
                return buttonCodes;
            }
        }
        JSONObject buttonJson = JSONObject.parseObject(text).getJSONObject(FORM_VAR_NAME).getJSONObject(BUTTON_VAR_NAME);
        String currentButtons = buttonJson.getString(PREVIOUS);
        if (Strings.isNullOrEmpty(currentButtons)) {
            return buttonCodes;
        }
        String[] buttonArray = currentButtons.split(",");
        List<String> buttonList = new ArrayList<>();
        for (String buttonCode : buttonArray) {
            if (!Strings.isNullOrEmpty(buttonCode)) {
                buttonList.add(buttonCode);
            }
        }
        if (isEntrust) {
            if (buttonList.contains(ButtonEnum.RETRIEVE_BTN.getCode())) {
                buttonList.remove(ButtonEnum.RETRIEVE_BTN.getCode());
            }
        }
        return buttonList;
    }

    /**
     * 添加自定义提交按钮
     *
     * @param list
     * @param commandContext
     */
    private void addCustomCommitButton(List<Button> list, CommandContext commandContext) {
        for (Button taskButton : list) {
            if (taskButton instanceof TaskSubmitButton) {
                List<Button> customerButtons = TaskButton.buildCustomerButtons(buttonVariable.getTextValue());
                if (customerButtons != null) {
                    List<Map<SequenceFlow, Boolean>> sequenceFlowList = commandContext.getProcessEngineConfiguration().getRuntimeService()
                            .getSubmitElement(taskEntity.getProcessDefinitionId(), taskEntity.getId(), buttonValidateParam.getBean(), "");

                    for (Map<SequenceFlow, Boolean> sequenceFlowBooleanMap : sequenceFlowList) {
                        for (Map.Entry<SequenceFlow, Boolean> sequenceFlowBooleanEntry : sequenceFlowBooleanMap.entrySet()) {
                            SequenceFlow sequenceFlow = sequenceFlowBooleanEntry.getKey();
                            for (Button button : customerButtons) {
                                TaskCustomSubmitButton taskCustomSubmitButton = (TaskCustomSubmitButton) button;
                                if (taskCustomSubmitButton.getNode().equals(sequenceFlow.getTargetRef())) {
                                    list.add(taskCustomSubmitButton);
                                }
                            }
                        }
                    }
                }
                break;
            }
        }
    }


    protected JSONObject getJsonObject(DelegateExecution execution, String variableName) {
        Object value = execution.getVariable(variableName);
        return (JSONObject) (value != null ? value : null);
    }

}
